#include <algorithm>
#include <functional>
#include <iostream>

#include "Testing.h"

namespace
{
    class Accumulator : public TestFramework::CasesAccumulator
    {
    public:
        virtual void AddCase(const TestFramework::TestCasePtr& testCase)
        {
            Cases.push_back(testCase);
        }

        virtual void Enumerate(TestFramework::CaseProcessor& processor)
        {
            for (TestFramework::TestCasesList::iterator iter = Cases.begin(); iter != Cases.end(); ++iter)
            {
                processor.Process(*iter);
            }
        }

        virtual void Clear()
        {
            Cases.clear();
        }

        virtual int GetCasesCount() const
        {
            return Cases.size();
        }

    private:
        TestFramework::TestCasesList Cases;
    };

    class CaseRanProcessor : public TestFramework::CaseProcessor
    {
    public:
        virtual void Process(const TestFramework::TestCasePtr& testCase)
        {
            testCase->TestMethod();
            std::cout << '.';
        }
    };

    class CaseResultProcessor : public TestFramework::CaseProcessor
    {
    public:
        CaseResultProcessor()
            : FailsCount(0)
            , SuccessCount(0)
        {
        }

        virtual void Process(const TestFramework::TestCasePtr& testCase)
        {
            testCase->IsFailed ? FailsCount++ : SuccessCount++;
        }

        int GetFailsCount() const
        {
            return FailsCount;
        }

        int GetSuccessCount() const
        {
            return SuccessCount;
        }

    private:
        int FailsCount;
        int SuccessCount;
    };
}

namespace TestFramework
{
    void TraceError(const std::string& description, const std::string& fileName, int line, const std::string& signature)
    {
        std::cout << std::endl << "Test failed: " << std::endl
            << '\t' << "Errot text: " << '\t' << description.c_str() << std::endl
            << '\t' << "File name: " << '\t' << fileName.c_str() << std::endl
            << '\t' << "Function name: " << '\t' << signature.c_str() << std::endl
            << '\t' << "Line: " << "\t\t" << line << std::endl;
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////

    TestManager::TestManager()
        : Failes(0)
        , Success(0)
    {
    }

    void TestManager::AddTest(const UnitTestPtr& test)
    {
        Tests.push_back(test);
    }

    void TestManager::RunTests()
    {
        Accumulator accumulator;
        for (UnitTestList::const_iterator iter = Tests.begin(); iter != Tests.end(); ++iter)
        {
            (*iter)->RegisterTests(accumulator);
            std::cout << (*iter)->GetName() << ":\t";
            CaseRanProcessor runner;
            accumulator.Enumerate(runner);
            CaseResultProcessor resultProcessor;
            accumulator.Enumerate(resultProcessor);
            Failes += resultProcessor.GetFailsCount();
            Success += resultProcessor.GetSuccessCount();
            std::cout << std::endl;
            accumulator.Clear();
        }
    }

    void TestManager::ShowResultSummary() const
    {
        std::cout << std::endl;
        std::cout << "Success:\t" << Success << std::endl;
        std::cout << "Failed:\t\t" << Failes << std::endl;
    }
}
